//============================================================================
// Copyright (C) 2008-2013 Brett R. Jones 
//
// Issued to public domain 2013
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================
#include "VxNetLib.h"
#include "VxSktUdp.h"
#include <VxUtilLib/VxParse.h>
#include <VxUtilLib/VxSktUtil.h>
#include <time.h>
#include <memory.h>
#include <stdio.h>

//============================================================================
//=== constructor ===//
VxSktUdp::VxSktUdp()
{
	m_eSktType = eSktTypeUdp;
}

//============================================================================
//=== destructor ===//
VxSktUdp::~VxSktUdp()
{
}

//============================================================================
RCODE VxSktUdp::UdpOpen( VxInetAddress& oLclIp, U16 u16Port )	
{
	struct addrinfo * poResultAddr = 0;

	RCODE rc = createSocket( oLclIp, u16Port, &poResultAddr );
	if( 0 == rc )
	{
		setReuseSocket( true );
		if( false == VxBindSkt( m_oSocket, oLclIp, u16Port ) )
		{
			rc = m_rcLastError;
		}
	}

	if( 0 == rc )
	{
		rc = startRecieve();
	}
	return rc;
}

//============================================================================
RCODE VxSktUdp::UdpOpenUnicast( VxInetAddress& oLclIp, U16 u16Port )
{
	struct addrinfo * poResultAddr = 0;
	RCODE rc = createSocket( oLclIp, u16Port, &poResultAddr );
	if( 0 == rc )
	{
		if( false == VxBindSkt( m_oSocket, oLclIp, u16Port ) )
		{
			rc = -1;
		}
	}
	if( 0 == rc )
	{
		rc = startRecieve();
	}
	if( rc )
	{
		log_msg( LOG_ERROR, "UdpOpenUnicast error %d", rc );
	}
	return rc;
}

//============================================================================
RCODE VxSktUdp::UdpOpenMulticastListen( VxInetAddress& oLclIp, U16 u16Port, const char * pIPv4MulticastGroup )	
{
	m_strMulticastGroupIp = pIPv4MulticastGroup;
	struct addrinfo * poResultAddr = 0;

	RCODE rc = createSocket( oLclIp, u16Port, &poResultAddr );
	if( 0 == rc )
	{
		setReuseSocket( true );
		if( false == VxBindSkt( m_oSocket, oLclIp, u16Port ) )
		{
			rc = -1;
		}
	}

	if( false == joinMulticastGroup( oLclIp, m_strMulticastGroupIp.c_str() ) )
	{
		rc = -1;
	}


	setAllowLoopback( true );

	if( 0 == rc )
	{
		rc = startRecieve();
	}
	return rc;
}

//============================================================================
RCODE VxSktUdp::createSocket( VxInetAddress& oLclIp, U16 u16Port, struct addrinfo ** ppoResultAddr )	
{
	m_rcLastError = 0;
	if( IsConnected() )
	{
		CloseSkt();
	}
	incRefCnt();
	m_oLclIp = oLclIp;
	m_oLclIp.toStdString( m_strLclIp );


	vx_assert( m_pfnRecieve );
	m_oLclIp.setPort( u16Port );
	m_oRmtIp.setPort( u16Port );
	m_strRmtIp = "";
	m_eSktCallbackReason = eSktCallbackReasonConnecting;

	struct addrinfo * poResultAddr;
	struct addrinfo oHints;
	VxFillHints( oHints, true );

	char as8Port[16];
	sprintf( as8Port, "%d", u16Port);

	getaddrinfo( NULL, as8Port, &oHints, &poResultAddr );
	* ppoResultAddr = poResultAddr;
	m_oSocket = socket(poResultAddr->ai_family, poResultAddr->ai_socktype, poResultAddr->ai_protocol);
	if( INVALID_SOCKET == m_oSocket )
	{
		// create socket error
		m_eSktCallbackReason = eSktCallbackReasonConnectError;
		m_rcLastError = VxGetLastError();
		log_msg( LOG_ERROR, "VxSktBase::UdpOpen: socket create error %s\n", DescribeSktError( m_rcLastError ) );
		m_pfnRecieve( this );
		return m_rcLastError;
	}
	return m_rcLastError;
}

//============================================================================
RCODE VxSktUdp::startRecieve( void )	
{
	m_bIsConnected = true;
	// tell user we connected
	m_pfnRecieve( this );

	// make a useful thread name
	std::string strThreadName;
	StdStringFormat( strThreadName, "VxSktBaseUDP%d", m_iSktId );
	return StartRecieveThread( strThreadName.c_str() );
}

//============================================================================
//! send data to given ip 
RCODE  VxSktUdp::SendTo(	const char *	pData,		// data to send
							int				iDataLen,	// data len
							const char *	pRmtIp, 	// destination ip in dotted format
							U16				u16Port )	// port to send to ( if 0 then port specified when opened )
{
	VxInetAddress oAddr( pRmtIp );
	return SendTo( pData, iDataLen, oAddr, u16Port );
}

//============================================================================
//! send data to given ip 
RCODE  VxSktUdp::sendToMulticast(	const char *	pData,		// data to send
									int				iDataLen,	// data len
									const char *	pRmtIp, 	// destination ip in dotted format
									U16				u16Port )	// port to send to ( if 0 then port specified when opened )
{
	VxInetAddress oAddr( pRmtIp );
	struct ip_mreq mreq;
	mreq.imr_multiaddr.s_addr = inet_addr( m_strMulticastGroupIp.c_str() );
	mreq.imr_interface.s_addr = inet_addr( pRmtIp );
	if( setsockopt( m_oSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,(char*)&mreq, sizeof(mreq)) < 0)
	{
		m_rcLastError = VxGetLastError();
		log_msg( LOG_SKT, "VxSktUdp::SendToMulticast setsockopt mreq failed %s\n", DescribeSktError( m_rcLastError ) );
	}

	return SendTo( pData, iDataLen, oAddr, u16Port );
}

//============================================================================
//! send data to given ip 
RCODE VxSktUdp::SendTo(		const char *	pData,		// data to send
							int				iDataLen,	// data len
							VxInetAddress&	oRmtIp, 	// destination ip in host ordered u32
							U16				u16Port )	// port to send to ( if 0 then port specified when opened )
{
	if(	( false == m_oSktRxThread.IsThreadCreated()) || 
		( false == IsConnected()) )
	{
		log_msg( LOG_ERROR, "VxSktUdp::SendTo: NOT CONNECTED OR INVALID\n" );
		return -1;
	}

	struct sockaddr_storage oSktAddr;
    oRmtIp.fillAddress( oSktAddr, u16Port );

	int iDataSentLen =  sendto( m_oSocket, pData, iDataLen, 0, (struct sockaddr *)&oSktAddr, sizeof(struct sockaddr_storage) );

	if( iDataSentLen !=  iDataLen )
	{
		//send error
		m_rcLastError = VxGetLastError();
		if( 0 == m_rcLastError )
		{
			m_rcLastError = iDataSentLen;
			log_msg( LOG_ERROR, "VxSktUdp::SendTo: Error 0.. assigning data sent as error %d\n", iDataSentLen );
		}
		else
		{
			log_msg( LOG_ERROR, "VxSktUdp::SendTo: Error %s\n", DescribeSktError( m_rcLastError ) );
		}
		return m_rcLastError;
	}
	m_u32RxLastActiveTime = (unsigned long)time( NULL );
	return 0;
}




